/*
 * Copyright 2002-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.context.support;

import java.io.IOException;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.ResourceEntityResolver;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

/**
 * {@link org.springframework.web.context.WebApplicationContext} implementation
 * which takes its configuration from XML documents, understood by an
 * {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}. This
 * is essentially the equivalent of
 * {@link org.springframework.context.support.AbstractXmlApplicationContext} for
 * a web environment.
 * 
 * <p>
 * By default, the configuration will be taken from
 * "/WEB-INF/applicationContext.xml" for the root context, and
 * "/WEB-INF/test-servlet.xml" for a context with the namespace "test-servlet"
 * (like for a DispatcherServlet instance with the servlet-name "test").
 * 
 * <p>
 * The config location defaults can be overridden via the
 * "contextConfigLocation" context-param of
 * {@link org.springframework.web.context.ContextLoader} and servlet init-param
 * of {@link org.springframework.web.servlet.FrameworkServlet}. Config locations
 * can either denote concrete files like "/WEB-INF/context.xml" or Ant-style
 * patterns like "/WEB-INF/*-context.xml" (see
 * {@link org.springframework.util.PathMatcher} javadoc for pattern details).
 * 
 * <p>
 * Note: In case of multiple config locations, later bean definitions will
 * override ones defined in earlier loaded files. This can be leveraged to
 * deliberately override certain bean definitions via an extra XML file.
 * 
 * <p>
 * <b>For a WebApplicationContext that reads in a different bean definition
 * format, create an analogous subclass of
 * {@link AbstractRefreshableWebApplicationContext}.</b> Such a context
 * implementation can be specified as "contextClass" context-param for
 * ContextLoader or "contextClass" init-param for FrameworkServlet.
 * 
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see #setNamespace
 * @see #setConfigLocations
 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
 * @see org.springframework.web.context.ContextLoader#initWebApplicationContext
 * @see org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
 */
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {

	// root context的默认配置地址。
	/** Default config location for the root context */
	public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

	// 为命名空间创建配置地址的默认前缀
	/** Default prefix for building a config location for a namespace */
	public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";

	// //为命名空间创建配置地址的默认后缀
	/** Default suffix for building a config location for a namespace */
	public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";

	/**
	 * 从一个XmlBeanDefinitionReader中获取bean定义。<br/>
	 * Loads the bean definitions via an XmlBeanDefinitionReader.
	 * 
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
	 * @see #initBeanDefinitionReader
	 * @see #loadBeanDefinitions
	 */
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
			throws IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(
				beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		/**
		 * 子类可以提供自定义Reader初始化的方法，然后加载处理具体的Bean定义。<br/>
		 * Allow a subclass to provide custom initialization of the reader,then
		 * proceed with actually loading the bean definitions.
		 */
		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader);
	}

	/**
	 * 初始化加载当前context的Bean定义的Reader，默认实现是空的。<br/>
	 * Initialize the bean definition reader used for loading the bean
	 * definitions of this context. Default implementation is empty.
	 * <p>
	 * Can be overridden in subclasses, e.g. for turning off XML validation or
	 * using a different XmlBeanDefinitionParser implementation.
	 * 
	 * @param beanDefinitionReader
	 *            the bean definition reader used by this context
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setValidationMode
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
	 */
	protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
	}

	/**
	 * 使用给定的XmlBeanDefinitionReader，加载Bean定义。<br/>
	 * Load the bean definitions with the given XmlBeanDefinitionReader.
	 * <p>
	 * The lifecycle of the bean factory is handled by the refreshBeanFactory
	 * method; therefore this method is just supposed to load and/or register
	 * bean definitions.
	 * <p>
	 * Delegates to a ResourcePatternResolver for resolving location patterns
	 * into Resource instances.
	 * 
	 * @throws org.springframework.beans.BeansException
	 *             in case of bean registration errors
	 * @throws java.io.IOException
	 *             if the required XML document isn't found
	 * @see #refreshBeanFactory
	 * @see #getConfigLocations
	 * @see #getResources
	 * @see #getResourcePatternResolver
	 */
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader)
			throws BeansException, IOException {
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			for (int i = 0; i < configLocations.length; i++) {
				reader.loadBeanDefinitions(configLocations[i]);
			}
		}
	}

	/**
	 * The default location for the root context is
	 * "/WEB-INF/applicationContext.xml", and "/WEB-INF/test-servlet.xml" for a
	 * context with the namespace "test-servlet" (like for a DispatcherServlet
	 * instance with the servlet-name "test").
	 */
	protected String[] getDefaultConfigLocations() {
		if (getNamespace() != null) {
			return new String[] { DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace()
					+ DEFAULT_CONFIG_LOCATION_SUFFIX };
		} else {
			return new String[] { DEFAULT_CONFIG_LOCATION };
		}
	}

}
